package reesoft.idgenerator.service.idstore;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import reesoft.common.code.ErrorCode;
import reesoft.common.util.ApplicationContextUtil;
import reesoft.common.util.StringUtil;
import reesoft.idgenerator.IdGenerateException;
import reesoft.idgenerator.dao.IdInfoRepository;
import reesoft.idgenerator.entity.IdInfo;

import java.util.HashMap;
import java.util.Map;

/**
 * 以 MySQL 数据库为媒介存储序号
 *
 * Created by reesoft on 2017-08-28
 */
public class MySqlIdStore extends BaseIdStore
{
    private static final Logger LOG = LoggerFactory.getLogger(MySqlIdStore.class);

    /**
     * 存储序号的数据表的操作对象
     */
    private IdInfoRepository idInfoRepository;

    /**
     * 构造函数
     */
    public MySqlIdStore()
    {
        MySqlIdStoreConfig config = ApplicationContextUtil.getBean("mySqlIdStoreConfig");

        if (config != null)
        {
            setIdInfoRepository(config.getIdInfoRepository());
        }
    }

    /**
     * 设置存储序号的数据表的操作对象
     */
    public void setIdInfoRepository(IdInfoRepository idInfoRepository)
    {
        this.idInfoRepository = idInfoRepository;
    }

    @Transactional
    @Override
    public void init(String name, long initValue, long maxValue) throws IdGenerateException
    {
        LOG.debug("MySqlIdStore.init, name:{}, initValue:{}, maxValue:{}", name, initValue, maxValue);

        if (idInfoRepository == null)
        {
            throw new IdGenerateException("Database connection is not ready");
        }

        if (StringUtil.isBlank(name))
        {
            throw new IdGenerateException(ErrorCode.INVALID_PARAMETER);
        }

        setMaxValue(maxValue);

        try
        {
            IdInfo idInfo = idInfoRepository.getByName(name);

            if (idInfo == null)
            {
                idInfo = new IdInfo();
                idInfo.setName(name);
                idInfo.setInitValue(initValue);
                idInfo.setMaxValue(maxValue);
                idInfo.setCurrentValue(initValue);
                idInfoRepository.addSelective(idInfo);

                LOG.info("MySqlIdStore.init, create ID store with name:{}, initValue:{}", name, initValue);
            }
            else if (idInfo.getCurrentValue() == null)
            {
                idInfo.setCurrentValue(initValue);
                idInfoRepository.updateSelective(idInfo);

                LOG.info("MySqlIdStore.init, update ID store with name:{}, currentValue:{}", name, initValue);
            }
            else if (idInfo.getCurrentValue() > idInfo.getMaxValue())
            {
                throw new IdGenerateException("Exceed max value");
            }
        }
        catch (IdGenerateException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOG.error(e.toString());
            throw new IdGenerateException("Failed to store value in database");
        }
    }

    @Override
    public boolean check(String name)
    {
        //LOG.debug("MySqlIdStore.check, name:" + name);

        if (idInfoRepository == null)
        {
            return false;
        }

        if (StringUtil.isBlank(name))
        {
            return false;
        }

        try
        {
            IdInfo idInfo = idInfoRepository.getByName(name);

            if (idInfo != null
                    && idInfo.getCurrentValue() != null
                    && idInfo.getCurrentValue() <= idInfo.getMaxValue())
            {
                return true;
            }
        }
        catch (Exception e)
        {
            LOG.error(e.toString());
        }

        return false;
    }

    @Override
    public long loadAndIncrease(String name, long amount) throws IdGenerateException
    {
        LOG.info("MySqlIdStore.loadAndIncrease, name:{}, amount:{}", name, amount);

        if (idInfoRepository == null)
        {
            throw new IdGenerateException("Database connection is not ready");
        }

        if (StringUtil.isBlank(name)
                || amount <= 0)
        {
            throw new IdGenerateException(ErrorCode.INVALID_PARAMETER);
        }

        try
        {
            Map<String, Object> param = new HashMap<>();
            param.put("name", name);
            param.put("amount", amount);
            idInfoRepository.updateSequenceValue(param);

            Long currentValue = (Long) param.get("currentValue");

            if (currentValue == null)
            {
                throw new IdGenerateException("ID not initialized");
            }

            if (Long.compareUnsigned(currentValue + amount, getMaxValue()) == 1)
            {
                throw new IdGenerateException("Exceed max value");
            }

            LOG.info("MySqlIdStore.loadAndIncrease, ID assigned with name:{}, currentValue:{}, amount:{}",
                    name, currentValue, amount);

            return currentValue;
        }
        catch (Exception e)
        {
            LOG.error(e.toString());
            throw new IdGenerateException("Failed to store value in database");
        }
    }
}
